#ifndef AVALON_HTTPSERVER_HPP
#define AVALON_HTTPSERVER_HPP
#include "Avalon/IO/Connection.hpp"
#include "Avalon/IO/ConnectionState.hpp"
#include "Avalon/SignalHandling/ConnectionGroup.hpp"
#include "Avalon/SignalHandling/TaskSignalHandler.hpp"
#include "Avalon/Threading/Async.hpp"
#include "Avalon/WebServices/WebServices.hpp"

namespace Avalon {
namespace WebServices {

  /*! \class HttpServer
   *  \brief Implements an HTTP server.
   *  \author Kamal Mansouri <kranar@hotmail.com>
   */
  class HttpServer : public IO::Connection {
    public:

      //! The default keep alive.
      static const boost::posix_time::time_duration KEEP_ALIVE;

      //! Signals an incoming HTTP request.
      /*!
        \param request The HTTP request.
        \param response The HTTP response.
      */
      typedef boost::signals2::signal<void (HttpServerRequest*,
        HttpServerResponse*)> HttpRequestSignal;

      //! Defines an HTTP request predicate.
      /*!
        \param request The request to test.
        \return <code>true</code> iff the <i>request</i> satisfies this
                predicate.
      */
      typedef boost::function<bool (const HttpServerRequest*)>
        HttpRequestPredicate;

      //! Constructs an HttpServer.
      /*!
        \param connection The ServerConnection accepting incoming connections.
        \param keepAlive The request keep alive.
      */
      HttpServer(IO::ServerConnection* connection,
        boost::posix_time::time_duration keepAlive = KEEP_ALIVE);

      virtual ~HttpServer();

      //! Starts the HttpServer.
      /*!
        \throw IOException if the server failed to start.
      */
      void Start();

      //! Receives HTTP requests where the request matches a predicate.
      /*!
        \param predicate The predicate to match the request against.
        \param slot The slot to connect.
        \return A connection receiving HTTP requests.
      */
      boost::signals2::connection ConnectRequestSignal(
        const HttpRequestPredicate& predicate,
        const HttpRequestSignal::slot_type& slot);

      virtual bool IsConnected();

      virtual void Close();

      virtual boost::signals2::connection ConnectClosedSignal(
        const ClosedSignal::slot_type& slot);

      virtual boost::signals2::connection ConnectClosedSignal(
        const ClosedSignal::slot_type& slot, bool* isConnected);

    private:
      friend class HttpServerResponse;
      struct RequestSignalEntry {
        HttpRequestPredicate m_predicate;
        HttpRequestSignal m_signal;

        RequestSignalEntry(const HttpRequestPredicate& predicate);
      };
      boost::recursive_mutex m_mutex;
      boost::scoped_ptr<IO::ServerConnection> m_connection;
      IO::ConnectionState m_connectionState;
      bool m_isConnected;
      bool m_isShuttingDown;
      std::vector<HttpClientChannel*> m_clients;
      SignalHandling::ConnectionGroup m_channelClosedConnections;
      std::vector<RequestSignalEntry*> m_requestSignals;
      boost::condition_variable_any m_closedCondition;
      ClosedSignal m_closedSignal;
      SignalHandling::TaskSignalHandler m_signalHandler;

      void Shutdown();
      void RemoveChannel(HttpClientChannel* channel);
      void SendResponse(HttpServerResponse* response,
        HttpClientChannel* channel);
      void OnConnectionAccepted(
        const Threading::Async<IO::Channel*>::Ptr& result);
      void OnConnectionClosed();
      void OnClientClosed(HttpClientChannel* channel);
      void OnClientRead(const Threading::Async<int>::Ptr& result,
        HttpClientChannel* channel);
  };
}
}

#endif // AVALON_HTTPSERVER_HPP
